Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(users): add support for tenant level users #6708

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

apoorvdixit88
Copy link
Contributor

@apoorvdixit88 apoorvdixit88 commented Dec 1, 2024

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

  • Add api to create tenant users
  • Add api to create orgs
  • Support tenant level entity for the user hierarchy

Additional Changes

  • This PR modifies the API contract
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Motivation and Context

Closes #6707

How did you test it?

Api for create tenant:

curl --location 'http://localhost:8080/user/tenant_signup' \
--header 'api-key: test_admin' \
--header 'Content-Type: application/json' \
--header 'x-tenant-id: test' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTJhMmViYzItOWZiZS00MDI0LThjODEtOWYwMzMyZTJlZDMxIiwicHVycG9zZSI6InRvdHAiLCJvcmlnaW4iOiJzaWduX3VwIiwicGF0aCI6W10sImV4cCI6MTczMzMwOTI2NSwidGVuYW50X2lkIjoicHVibGljIn0.3B918rdY_XM0dRH4yx9O7Nujd3FbLdmKUmBY52KVKAo' \
--data-raw '{
    "name" : "tf2",
    "email" : "[email protected]",
    "password": "Pass@321"
}'

Response: 200 Ok, if tenant_admin got created successfully
A tenant can login with email and password and continue with 2FA, he will land into any one of organization existing for tenant.

Create Org Api, (works for tenant users only)

curl --location 'http://localhost:8080/user/create_org' \
--header 'x-tenant-id: test' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer JWT' \
--header 'Cookie: Cookie' \
--data '{
    "organization_name": "ten",
    "merchant_name": "pp_ten"
}'

Response will be 200 OK if the org got created success fully (1 org with 1 merchant and 1 profile)

After getting the login token tenant admin can switch to any org in the tenancy.

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible

@apoorvdixit88 apoorvdixit88 added C-feature Category: Feature request or enhancement A-users Area: Users labels Dec 1, 2024
@apoorvdixit88 apoorvdixit88 self-assigned this Dec 1, 2024
@apoorvdixit88 apoorvdixit88 requested review from a team as code owners December 1, 2024 21:16
crates/router/src/core/user.rs Outdated Show resolved Hide resolved
crates/router/src/core/user.rs Outdated Show resolved Hide resolved
crates/router/src/core/user.rs Outdated Show resolved Hide resolved
crates/router/src/core/user.rs Outdated Show resolved Hide resolved
crates/router/src/core/user.rs Outdated Show resolved Hide resolved
Comment on lines 874 to 879
EntityType::Tenant => {
return Err(UserErrors::InvalidRoleOperationWithMessage(
"Tenant roles are not allowed for this operation".to_string(),
)
.into());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we throw error here? Is dashboard handling this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query above it will fetch 0 user_roles for entity type tenant where status is invitation_sent, so we should throw error here instead of sending empty vector, if we have such user role.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status should be 500

crates/router/src/types/transformers.rs Outdated Show resolved Hide resolved
crates/router/src/types/domain/user/decision_manager.rs Outdated Show resolved Hide resolved
pub organization_name: String,
pub organization_details: Option<pii::SecretSerdeValue>,
pub metadata: Option<pii::SecretSerdeValue>,
pub merchant_name: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be a secret.

Comment on lines 874 to 879
EntityType::Tenant => {
return Err(UserErrors::InvalidRoleOperationWithMessage(
"Tenant roles are not allowed for this operation".to_string(),
)
.into());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status should be 500

@@ -1869,6 +1869,10 @@ impl User {
.service(
web::resource("/internal_signup").route(web::post().to(user::internal_user_signup)),
)
.service(
web::resource("/create_tenant").route(web::post().to(user::create_tenant_user)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_merchant -> creates merchant account
create_org -> creates org account
create_tenant -> creates user ??

@@ -124,18 +124,34 @@ impl JWTFlow {
next_flow: &NextFlow,
user_role: &UserRole,
) -> UserResult<Secret<String>> {
let (merchant_id, profile_id) =
utils::user_role::get_single_merchant_id_and_profile_id(state, user_role).await?;
let (org_id, merchant_id, profile_id) = match user_role.entity_type {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be simplified.

Comment on lines 1285 to 1301
let key_store = state
.store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Error while fetching the key store by merchant_id")?;

let merchant_account = state
.store
.find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Error while fetching the merchant_account by merchant_id")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be skipped is below function only requires merchant id and org id and not the whole merchant account.

ThisIsMani
ThisIsMani previously approved these changes Dec 4, 2024
crates/api_models/src/user.rs Outdated Show resolved Hide resolved
crates/router/src/utils/user.rs Outdated Show resolved Hide resolved
crates/router/src/utils/user.rs Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-users Area: Users C-feature Category: Feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat(users): support tenant level users
4 participants